//广播模块：负责客户端的消息发送，支持群聊和私聊功能。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include "broadcast.h"
#include <sys/socket.h>
#include"client_handler.h"
#include "config.h"

extern pthread_mutex_t mutex;   // 声明一个外部的mutex变量（实际定义在server.c中）

/**
 *  获取当前在线用户列表，并将其存储在指定缓冲区中。
 * 
 * 该函数会遍历客户端列表，找出所有在线且已认证的客户端，
 * 将它们的用户名拼接成一个字符串，存储在传入的缓冲区中。
 * 
 *  buffer 用于存储在线用户列表的缓冲区，以 null 结尾的字符串。
 *  clients 客户端列表，包含所有客户端信息的数组。
 */
void get_online_users(char *buffer, Client *clients) {
    // 初始化缓冲区，将第一个字符设为字符串结束符，确保缓冲区为空
    buffer[0] = '\0';
    // 向缓冲区添加提示信息，表明接下来是在线用户列表
    strcpy(buffer, "当前在线用户: ");
    
    // 加锁，确保在遍历客户端列表时数据的一致性
    pthread_mutex_lock(&mutex);
    
    int count = 0;  // 用于记录在线用户的数量

    // 遍历客户端列表
    for (int i = 0; i < MAX_CLIENTS; i++) {
        // 检查当前客户端是否在线且已认证
        if (clients[i].socket != -1 && clients[i].is_authenticated) {
            // 如果已经有在线用户，添加逗号和空格作为分隔符
            if (count > 0) {
                // 若不是第一个用户，在用户名前添加逗号和空格
                // 确保在线用户列表的格式为 "用户1, 用户2, 用户3, ..."
                strcat(buffer, ", ");
            }
            // 将当前在线用户的用户名添加到缓冲区
            strcat(buffer, clients[i].username);
            // 在线用户数量加 1
            count++;
        }
    }
    // 解锁，允许其他线程访问客户端列表
    pthread_mutex_unlock(&mutex);
    // 在在线用户列表末尾添加换行符
    strcat(buffer, "\n");
}

/**
 *  向所有在线且已认证的客户端广播消息，支持私聊消息标记。
 * 
 * 该函数会根据传入的参数构造完整的消息内容，然后遍历客户端列表，
 * 将消息发送给所有在线且已认证的客户端，可选择排除发送者本身。
 * 
 *  sender_name 发送者的用户名，若为 NULL 则直接使用原始消息。
 *  message 要广播的消息内容，以 null 结尾的字符串。
 *   sender_socket 发送者的套接字文件描述符，-1 表示不排除发送者。
 *  is_private 标记消息是否为私聊消息，非零表示私聊消息。
 * clients 客户端列表，包含所有客户端信息的数组。
 */
    void broadcast_message(char *sender_name, char *message, int sender_socket, 
                      int is_private, Client *clients) {
    // 加锁，确保在广播消息过程中客户端列表数据的一致性
    pthread_mutex_lock(&mutex);
    
    // 定义一个缓冲区，用于存储构造后的完整消息
    char full_message[BUFFER_SIZE * 2];
    // 检查发送者用户名是否存在
    if (sender_name) {
        // 根据是否为私聊消息构造不同格式的消息
        if (is_private) 
            // 构造私聊消息，格式为 [私聊][发送者用户名]:消息内容
            sprintf(full_message, "[私聊][%s]:%s\n", sender_name, message);
        else 
            // 构造普通消息，格式为 [发送者用户名]:消息内容
            sprintf(full_message, "[%s]:%s\n", sender_name, message);
    } else {
        // 若发送者用户名不存在，直接将原始消息复制到完整消息缓冲区
        strcpy(full_message, message);
    }
    
    // 遍历客户端列表
    for (int i = 0; i < MAX_CLIENTS; i++) {
        // 检查客户端是否在线、已认证，并且根据 sender_socket 决定是否排除发送者
        if (clients[i].socket != -1 && clients[i].is_authenticated && 
            (sender_socket == -1 || clients[i].socket != sender_socket)) {
            // 向符合条件的客户端发送完整消息
            send(clients[i].socket, full_message, strlen(full_message), 0);
        }
    }
    
    // 解锁，允许其他线程访问客户端列表
    pthread_mutex_unlock(&mutex);
}

/**
 *  向指定用户发送私聊消息。
 * 
 * 该函数会在客户端列表中查找发送者和接收者，若两者都在线且已认证，
 * 则向接收者发送包含发送者信息的私聊消息，同时向发送者反馈发送的消息内容。
 * 最后，将发送者和接收者的私聊模式标记为开启，并记录对方的用户名。
 * 
 *  sender_name 发送者的用户名，以 null 结尾的字符串。
 *  target_name 接收者的用户名，以 null 结尾的字符串。
 *  message 要发送的私聊消息内容，以 null 结尾的字符串。
 *  clients 客户端列表，包含所有客户端信息的数组。
 */
void send_private_message(char *sender_name, char *target_name, 
                         char *message, Client *clients) {
    // 加锁，确保在查找和发送消息过程中客户端列表数据的一致性
    pthread_mutex_lock(&mutex);
    
    // 初始化发送者和接收者在客户端列表中的索引为 -1，表示未找到
    int sender_idx = -1, target_idx = -1;
    // 遍历客户端列表
    for (int i = 0; i < MAX_CLIENTS; i++) {
        // 检查当前客户端是否在线且已认证
        if (clients[i].socket != -1 && clients[i].is_authenticated) {
            // 查找发送者的索引
            if (strcmp(clients[i].username, sender_name) == 0) {
                sender_idx = i;
            }
            // 查找接收者的索引
            if (strcmp(clients[i].username, target_name) == 0) {
                target_idx = i;
            }
        }
    }
    
    // 若发送者和接收者都找到
    if (sender_idx != -1 && target_idx != -1) {
        // 用于存储完整的私聊消息
        char full_msg[BUFFER_SIZE * 2];
        // 构造发送给接收者的消息，包含发送者用户名和消息内容
        sprintf(full_msg, "[私聊][%s]:%s\n", sender_name, message);
        // 向接收者发送私聊消息
        send(clients[target_idx].socket, full_msg, strlen(full_msg), 0);
        
        // 构造发送给发送者的反馈消息，显示“你”作为发送者
        sprintf(full_msg, "[私聊][你]:%s\n", message);
        // 向发送者发送反馈消息
        send(clients[sender_idx].socket, full_msg, strlen(full_msg), 0);
        
        // 标记发送者进入私聊模式
        clients[sender_idx].in_private_mode = 1;
        // 记录发送者的私聊目标为接收者用户名
        strcpy(clients[sender_idx].private_target, target_name);
        // 标记接收者进入私聊模式
        clients[target_idx].in_private_mode = 1;
        // 记录接收者的私聊目标为发送者用户名
        strcpy(clients[target_idx].private_target, sender_name);
    }
    
    // 解锁，允许其他线程访问客户端列表
    pthread_mutex_unlock(&mutex);
}